package com.transfiguro.converters;

import ch.qos.logback.core.StopWatch;
import com.transfiguro.InitType;
import com.transfiguro.datamodel.csmodels.CSVariable;
import com.transfiguro.datamodel.javamodels.Variable;
import com.transfiguro.exceptions.ConversionFailedException;
import com.transfiguro.utilities.FileUtilities;
import com.transfiguro.utilities.MappingXMLParser;
import japa.parser.ast.expr.ObjectCreationExpr;
import japa.parser.ast.type.ClassOrInterfaceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Modifier;

/**
 * An implementation of {@link Converter}. This class provides methods to convert a
 * {@link Variable} to {@link CSVariable}
 */
public class VariableConverter implements Converter {
    private final Logger logger = LoggerFactory.getLogger(VariableConverter.class);
    private File variableXMLMapping;
    private static MappingXMLParser xmlParser;
    private Variable variable;

    private CSVariable csVariable;

    /**
     * Constructor
     *
     * @param var  The Variable to be converted
     */
    public VariableConverter(final Variable var) {
        variable = var;
        csVariable = new CSVariable();
        init();
    }

    /**
     * Initializer for various private variables
     */
    private void init() {
        final ClassPathResource resource = new ClassPathResource("mappingxmls/VariableMapping.xml");
        final InputStream stream = ClassLoader.getSystemResourceAsStream("mappingxmls/VariableMapping.xml");
        final File tempFile = new File(".", "BaseMethodMapping.xml");
        FileUtilities.writeTempFile(stream, tempFile);
        variableXMLMapping = tempFile;
        xmlParser = new MappingXMLParser(variableXMLMapping);
    }

    /**
     * This method performs the conversion from Variable to CSVariable
     *
     * @return  <tt>True</tt> if the conversion was successful
     * @throws ConversionFailedException  if there was an error in conversion
     */
    public boolean convert() throws ConversionFailedException {
        boolean result = true;
        //Not the perfect way of handling nulls
        final StopWatch stopWatch = new StopWatch("VariableConversionTimer");
        if (variable.getType() != null) {
            if (xmlParser.requiresConversion("type", variable.getType())) {
                csVariable.setType(xmlParser.getTo("type", variable.getType()));
            } else {
                csVariable.setType(variable.getType());
            }
        }


        if (!Modifier.toString(variable.getAccessModifier()).equals("")) {
            StringBuilder modifier = new StringBuilder();
            for (final String mod : Modifier.toString(variable.getAccessModifier()).split(" "))
                if (xmlParser.requiresConversion("accessModifier", mod.trim())) {
                    modifier.append(xmlParser.getTo("accessModifier", mod)).append(" ");
                } else {
                    modifier.append(mod).append(" ");
                }
            csVariable.setAccessModifier(modifier.toString().trim());
        }

        if (xmlParser.requiresConversion("name", variable.getVariableName())) {
            csVariable.setVariableName(xmlParser.getTo("name", variable.getVariableName()));
        } else {
            csVariable.setVariableName(variable.getVariableName());
        }

        //Copying value directly for now. This may require modification to accommodate value conversion
        if(variable.isHasValue()) {
            csVariable.setHasValue(true);
            //Check the type of expression and convert it
            String convertedValue = "";
            InitType initType;
            try{
                initType = InitType.valueOf(variable.getValue().getClass().getSimpleName());
            } catch (final IllegalArgumentException e) {
                initType = InitType.DefaultExpression;
            }
            logger.info("Var Type: {}", variable.getValue().getClass().getSimpleName());
            switch (initType) {
                case ObjectCreationExpr:
                    ObjectCreationExpr expression = (ObjectCreationExpr) variable.getValue();
                    logger.info("Expr: {}", expression.getType().toString());
                    if(xmlParser.requiresConversion("type", expression.getType().toString().trim())) {
                        expression.setType(new ClassOrInterfaceType(xmlParser.getTo("type",
                                        expression.getType().toString().trim())));
                    }
                    convertedValue = expression.toString();
                    break;
                case DefaultExpression:
                    convertedValue = variable.getValue().toString().trim();
                    break;
            }
            csVariable.setValue(convertedValue);
        }
        stopWatch.stop();
        csVariable.setConversionTime(stopWatch.getResultInSeconds());
        return result;
    }

    /**
     * Getter for csVariable
     *
     * @return
     */
    public CSVariable getCsVariable() {
        return csVariable;
    }
}
